/*  Game Editor for yALB - Yet Another List Builder (roster Editor for Wargames/tabletop games).
    Copyright (C) 2008-2012  Daniel Ricardo Castro Alvarado

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.*/

#include <QFile>
#include <QDataStream>
#include <QFileDialog>
#include <QtDebug>
#include <QList>
#include <QMessageBox>
#include <QXmlStreamReader>
#include <QDesktopWidget>
#include <QLibraryInfo>

#include "gameeditor.h"
#include "ui_gameeditor.h"

#include "other/structreader.h"
#include "other/structwriter.h"

GameEditor::GameEditor(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::GameEditor)
{
    ui->setupUi(this);

    isSaved = true;

    // Center screen and contents of scrollarea
    setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), QDesktopWidget().availableGeometry()));


    // Signal informationChanged()
    connect(ui->infGameName, SIGNAL(textEdited(QString)), this, SLOT(setUnsaved()));
    connect(ui->infCreator, SIGNAL(textEdited(QString)), this, SLOT(setUnsaved()));
    connect(ui->infVersion, SIGNAL(textEdited(QString)), this, SLOT(setUnsaved()));
    connect(ui->infWeb, SIGNAL(textEdited(QString)), this, SLOT(setUnsaved()));
    connect(ui->infComments, SIGNAL(textChanged()), this, SLOT(setUnsaved()));

    itemSelector = new ItemSelector(items, itemCategories);

    unitCategoryWidget = new CategoryWidget(unitCategories);
    itemCategoryWidget = new CategoryWidget(itemCategories);
    optionCategoryWidget = new CategoryWidget(optionCategories);
    ui->tabUnitCategories->layout()->addWidget(unitCategoryWidget);
    ui->tabItemCategories->layout()->addWidget(itemCategoryWidget);
    ui->tabOptionCategories->layout()->addWidget(optionCategoryWidget);

    columnWidget = new ColumnWidget(columnCollection);
    ui->tabColumns->layout()->addWidget(columnWidget);

    unitPropertyWidget = new PropertyWidget(unitProperties);
    optionPropertyWidget = new PropertyWidget(optionProperties);
    itemPropertyWidget = new PropertyWidget(itemProperties);
    ui->tabUnitProperties->layout()->addWidget(unitPropertyWidget);
    ui->tabOptionProperties->layout()->addWidget(optionPropertyWidget);
    ui->tabItemProperties->layout()->addWidget(itemPropertyWidget);

    optionWidget = new OptionWidget(options, *itemSelector);
    ui->tabOptions->layout()->addWidget(optionWidget);

    itemWidget = new ItemWidget(items, *itemSelector);
    ui->tabItems->layout()->addWidget(itemWidget);

    textWidget = new GlobalTextWidget(globalTexts);
    ui->tabTexts->layout()->addWidget(textWidget);

    connect(&optionCategories, SIGNAL(categoryAdded(GameCategory,QString)),
            optionWidget, SLOT(addCategory(GameCategory,QString)));
    connect(&optionCategories, SIGNAL(categoryUpdated(QString,GameCategory,QString)),
            optionWidget, SLOT(updateCategory(QString,GameCategory,QString)));
    connect(&optionCategories, SIGNAL(categoryRemoved(QString)),
            optionWidget, SLOT(removeCategory(QString)));

    connect(&optionProperties, SIGNAL(propertyAdded(GameProperty,QString)),
            optionWidget, SLOT(addProperty(GameProperty,QString)));
    connect(&optionProperties, SIGNAL(propertyUpdated(QString,GameProperty,QString)),
            optionWidget, SLOT(updateProperty(QString,GameProperty,QString)));
    connect(&optionProperties, SIGNAL(propertyRemoved(QString)),
            optionWidget, SLOT(removeProperty(QString)));

    connect(&itemCategories, SIGNAL(categoryAdded(GameCategory,QString)),
            itemWidget, SLOT(addCategory(GameCategory,QString)));
    connect(&itemCategories, SIGNAL(categoryUpdated(QString,GameCategory,QString)),
            itemWidget, SLOT(updateCategory(QString,GameCategory,QString)));
    connect(&itemCategories, SIGNAL(categoryRemoved(QString)),
            itemWidget, SLOT(removeCategory(QString)));

    connect(&itemProperties, SIGNAL(propertyAdded(GameProperty,QString)),
            itemWidget, SLOT(addProperty(GameProperty,QString)));
    connect(&itemProperties, SIGNAL(propertyUpdated(QString,GameProperty,QString)),
            itemWidget, SLOT(updateProperty(QString,GameProperty,QString)));
    connect(&itemProperties, SIGNAL(propertyRemoved(QString)),
            itemWidget, SLOT(removeProperty(QString)));

    /*connect(&columnCollection, SIGNAL(columnAdded(Column,QString)),
            optionWidget, SLOT(addColumn(Column,QString)));
    connect(&columnCollection, SIGNAL(columnUpdated(QString,Column,QString)),
            optionWidget, SLOT(updateColumn(QString,Column,QString)));
    connect(&columnCollection, SIGNAL(columnRemoved(QString)), optionWidget,
            SLOT(removeColumn(QString)));

    connect(&columnCollection, SIGNAL(columnAdded(Column,QString)),
            itemWidget, SLOT(addColumn(Column,QString)));
    connect(&columnCollection, SIGNAL(columnUpdated(QString,Column,QString)),
            itemWidget, SLOT(updateColumn(QString,Column,QString)));
    connect(&columnCollection, SIGNAL(columnRemoved(QString)), itemWidget,
            SLOT(removeColumn(QString)));*/

    connect(&unitCategories, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&itemCategories, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&optionCategories, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&columnCollection, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&globalTexts, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&items, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&options, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&unitProperties, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&itemProperties, SIGNAL(modified()), this, SLOT(setUnsaved()));
    connect(&optionProperties, SIGNAL(modified()), this, SLOT(setUnsaved()));

    isSaved = true;

    // Language files. Based on http://developer.qt.nokia.com/wiki/How_to_create_a_multi_language_application

    // - Create the languages menu with the default language (English)
    languageGroup = new QActionGroup(ui->menuLanguage);
    QAction *defaultLang = new QAction("English", languageGroup);
    defaultLang->setCheckable(true);
    defaultLang->setChecked(true);
    defaultLang->setData(QVariant("en"));
    ui->menuLanguage->addAction(defaultLang);

    // - Look for language  files located in "languages" directory (if it does not exist, will search in the application directory)

    QDir dir(QApplication::applicationDirPath());
    dir.cd("languages");

    // - Language files should be named gameeditor_language.qm (ISO 639-1 Code)

    QStringList languageFiles = dir.entryList(QStringList("gameeditor_*.qm"));
    QString systemLanguage = QLocale::system().name();

    for (int i = 0; i < languageFiles.size(); ++i) {

            QString locale = languageFiles.at(i);
            locale.truncate(locale.lastIndexOf('.'));
            locale.remove(0, locale.indexOf('_') + 1);

            QString lang = QLocale::languageToString(QLocale(locale).language());

            QAction *action = new QAction(lang, languageGroup);
            action->setCheckable(true);
            action->setData(locale);

            ui->menuLanguage->addAction(action);

            // - Load the system language if available
            if(systemLanguage.contains(locale)) {
                loadLanguage(locale);
                action->setChecked(true);
            }
    }

    // - Set up triggered signal

    connect(languageGroup, SIGNAL(triggered(QAction *)), this, SLOT(languageChanged(QAction *)));

    //QString path = QFileDialog::getOpenFileName(this);
    //QString path = "D:/yALB - Yet Another List Builder/Betas/yALB 0.6.9.1 (20 03 2011)/ej/es/8/2.lba";
//    qDebug() << "Directorio: " << path;
//    QFile file(path);
//    file.open(QFile::ReadOnly);
//    QDataStream d(&file);
//    d.setByteOrder(QDataStream::LittleEndian);
//    qDebug() << QString::fromUtf8(readString(&d));

//    while(d.atEnd() == false) {
//        char *buff = new char[50];
//        //uint i;
//        qDebug() << "return: " << d.readRawData(buff, 50);
//        qDebug() << QString::fromUtf8(buff);
//        delete[] buff;
//    }

    //file.close();
}

GameEditor::~GameEditor()
{
    if (languageGroup)
        delete languageGroup;
    delete ui;
}

void GameEditor::loadLanguage(QString language) {
    qApp->removeTranslator(&qtTranslator);
    qApp->removeTranslator(&editorTranslator);

    if(!language.isEmpty()) {
        qtTranslator.load("qt_" + language,
                          QLibraryInfo::location(QLibraryInfo::TranslationsPath));
        qApp->installTranslator(&qtTranslator);
        editorTranslator.load("gameeditor_" + language, QApplication::applicationDirPath() + "/languages");
        qApp->installTranslator(&editorTranslator);
        currentLanguage = language;
    }
}

void GameEditor::languageChanged(QAction *action) {
    if(action) {
        QString newLanguage = action->data().toString();
        if(currentLanguage != newLanguage)
            loadLanguage(newLanguage);
    }
}

void GameEditor::setSaved(bool saved) {
    if(isSaved != saved) {
        isSaved = saved;
        ui->actionSave->setEnabled(!saved);
    }
}

bool GameEditor::unsavedFileMessage() {
    // This function returns true if the application can proceed or returns false if it has to abort the operation
    if(!isSaved) {
        QMessageBox::StandardButton result = QMessageBox::warning(this, tr("Game File not saved"),
                                                                  tr("The current game system is not saved. Would you like to save it before proceeding?"),
                                                                  QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
        if(result == QMessageBox::Yes) {
            on_actionSave_triggered();
            return true;
        } else if(result == QMessageBox::Cancel)
            return false;
        else
            return true;
    } else
        return true;
}

void GameEditor::newGameFile() {
    itemCategories.clearAll();
    optionCategories.clearAll();
    unitCategories.clearAll();
    columnCollection.clearAll();
    itemProperties.clearAll();
    optionProperties.clearAll();
    unitProperties.clearAll();
    items.clearAll();
    options.clearAll();
    globalTexts.clearAll();

    ui->infGameName->setText("");
    ui->infCreator->setText("");
    ui->infVersion->setText("");
    ui->infWeb->setText("");
    ui->infComments->setPlainText("");

    unitCategoryWidget->clearCategoryForm();
    optionCategoryWidget->clearCategoryForm();
    itemCategoryWidget->clearCategoryForm();
    columnWidget->clearColumnForm();
    unitPropertyWidget->clearPropertyForm();
    optionPropertyWidget->clearPropertyForm();
    itemPropertyWidget->clearPropertyForm();
    optionWidget->clearOptionForm();
    itemWidget->clearItemForm();
    textWidget->clearGlobalTextForm();

    savedFile = "";
    setSaved(true);


    /*for(int i = 0; i<3; ++i) {
        if(ui->catList->topLevelItem(i)) {
            for(int a = 0; a<ui->catList->topLevelItem(i)->childCount(); ++a) {
                gameManager->removeCategory(ui->catList->topLevelItem(i)->child(a), false, false);
            }
            qDeleteAll(ui->catList->topLevelItem(i)->takeChildren());
        }
    }

    clearColumnForm();
    for(int i = 0; i<ui->colList->count(); ++i) {
        gameManager->removeColumn(ui->colList->item(i), false, false);
    }
    ui->colList->clear();
    qDeleteAll(ui->propList->topLevelItem(0)->takeChildren());

    clearPropertyForm();
    for(int i = 1; i<3; ++i) {
        for(int a = 0; a<ui->propList->topLevelItem(i)->childCount(); ++a) {
            gameManager->removeProperty(ui->propList->topLevelItem(i)->child(a), false, false);
        }
        qDeleteAll(ui->propList->topLevelItem(i)->takeChildren());
    }

    clearOptionForm();
    for(int i = 0; i<ui->optList->topLevelItem(0)->childCount(); ++i) {
        gameManager->removeOption(ui->optList->topLevelItem(0)->child(i), false, false);
    }    
    qDeleteAll(ui->optList->topLevelItem(0)->takeChildren());

    int count = ui->optList->topLevelItemCount();

    for(int i = 1; i<count; ++i) {
        delete ui->optList->topLevelItem(1);
    }

    clearItemForm();
    for(int i = 0; i<ui->itemList->topLevelItem(0)->childCount(); ++i) {
        gameManager->removeItem(ui->itemList->topLevelItem(0)->child(i), false, false);
    }
    qDeleteAll(ui->itemList->topLevelItem(0)->takeChildren());

    count = ui->itemList->topLevelItemCount();

    for(int i = 1; i<count; ++i) {
        delete ui->itemList->topLevelItem(1);
    }

    globalTexts.clear();
    for(int i = ui->globalTextsGrid->rowCount() -1; i>=0; --i) {
        ui->globalTextsGrid->removeRow(i);
    }*/


}

bool GameEditor::saveGameFile(const QString &path) {

    QFile file(path);
    if(file.open(QFile::WriteOnly)) {

        QXmlStreamWriter writer(&file);
        StructWriter sw(writer);

        writer.writeStartDocument();
        writer.writeStartElement("yALBGameEditor");
        writer.writeAttribute("version", "0.1");

        sw.writeFileInformation(FileInformation(ui->infGameName->text(),
                                                ui->infCreator->text(),
                                                ui->infVersion->text(),
                                                ui->infWeb->text(),
                                                ui->infComments->toPlainText()));
        sw.writeUnitCategories(unitCategoryWidget->getOrderedCategories());
        sw.writeOptionCategories(optionCategoryWidget->getOrderedCategories());
        sw.writeItemCategories(itemCategoryWidget->getOrderedCategories());

        sw.writeColumns(columnWidget->getOrderedColumns());

        sw.writeUnitProperties(unitPropertyWidget->getOrderedProperties());
        sw.writeOptionProperties(optionPropertyWidget->getOrderedProperties());
        sw.writeItemProperties(itemPropertyWidget->getOrderedProperties());

        sw.writeOptions(optionWidget->getOrderedOptions());

        sw.writeItems(itemWidget->getOrderedItems());

        sw.writeGlobalTexts(textWidget->getOrderedTexts());

        writer.writeEndDocument();
        file.close();
        setSaved(true);
        savedFile = path;
        ui->statusBar->showMessage(tr("File %1 saved successfully").arg(path), 10000);
        return true;
    } else
        return false;
}

bool GameEditor::openGameFile(const QString &path) {
    newGameFile();
    FileInformation information;
    GameFileStructs fileStructs;
    fileStructs.columns = &columnCollection;
    fileStructs.globalTexts = &globalTexts;
    fileStructs.itemCategories = &itemCategories;
    fileStructs.itemProperties = &itemProperties;
    fileStructs.items = &items;
    fileStructs.optionCategories = &optionCategories;
    fileStructs.optionProperties = &optionProperties;
    fileStructs.options = &options;
    fileStructs.unitCategories = &unitCategories;
    fileStructs.unitProperties = &unitProperties;

    if(StructReader::readGameFile(fileStructs, information, path, this) == StructReader::Success) {
        ui->infGameName->setText(information.name);
        ui->infCreator->setText(information.creator);
        ui->infVersion->setText(information.version);
        ui->infWeb->setText(information.website);
        ui->infComments->setPlainText(information.comments);
        setSaved(true);
        savedFile = path;
        ui->statusBar->showMessage(tr("File %1 opened successfully").arg(path), 10000);
        return true;
    } else {
        ui->statusBar->showMessage(tr("There was a problem while loading file %1").arg(path), 10000);
        return false;
    }
}

/*char* readString(QDataStream *data) {
    char *str = NULL;
    qint8 length;
    *data >> length;
    qDebug() << "readString() length = " << length;
    if(length > 0) {
        str = new char[length -1];
        data->readRawData(str, length);
        str[length] = '\0';
    }
    return str;
}
void writeString(QDataStream *data, QString string) {
    qint8 length = string.size();
    *data << length;
    QByteArray arr = string.toUtf8();
    for(int i = 0; i<arr.size(); ++i) {
        *data << (qint8)arr[i];
    }
}*/

void GameEditor::on_actionNew_triggered()
{
    if (unsavedFileMessage())
        newGameFile();
}

void GameEditor::on_actionOpen_triggered()
{
    if (unsavedFileMessage()) {
        QString path = QFileDialog::getOpenFileName(this, tr("Open Game File"), QString(), tr("yALB Game File (*.ygf)"));
        if(!path.isEmpty()){
            openGameFile(path);
        }
    }
}

void GameEditor::on_actionSave_triggered()
{
    if(!savedFile.isEmpty())
        saveGameFile(savedFile);
    else {
        on_actionSaveAs_triggered();
    }
}

void GameEditor::on_actionSaveAs_triggered()
{
    QString path = QFileDialog::getSaveFileName(this, tr("Save Game File"), QString(), tr("yALB Game File (*.ygf)"));
    if (!path.isEmpty())
        saveGameFile(path);
}

void GameEditor::on_actionExit_triggered()
{
    this->close();
}

void GameEditor::closeEvent(QCloseEvent *event) {
    if (unsavedFileMessage())
        event->accept();
    else
        event->ignore();
}

void GameEditor::changeEvent(QEvent *event) {
    if(event->type() == QEvent::LanguageChange)
        ui->retranslateUi(this);
    QMainWindow::changeEvent(event);
}
